'use strict';

const BaseService = require('../../libs/base/BaseService');
const redis = require('ioredis');
const fs = require('fs');
const path = require('path');

/**
 * Redis服务，提供Redis数据库操作
 */
class Redis extends BaseService {
  /**
   * 构造函数
   */
  constructor() {
    super('Redis');

    this._client = null;
    this._subscriber = null;
    this._config = this._loadConfig();
    this._pubsubCallbacks = new Map();
  }

  /**
   * 加载Redis配置
   * @returns {object} Redis配置
   * @private
   */
  _loadConfig() {
    try {
      const baseConfigPath = path.resolve(process.cwd(), 'config/baseConfig.json');
      if (fs.existsSync(baseConfigPath)) {
        const configContent = fs.readFileSync(baseConfigPath, 'utf8');
        const config = JSON.parse(configContent);

        if (config.redis) {
          // 设置默认值
          return {
            host: config.redis.host || 'localhost',
            port: config.redis.port || 6379,
            password: config.redis.password || '',
            db: config.redis.db || 0,
            // 重连策略
            reconnectStrategy: function (times) {
              const delay = Math.min(times * 100, 3000);
              return delay;
            }
          };
        }
      }
    } catch (error) {
      console.error('[Redis] 加载Redis配置失败:', error);
    }

    // 返回默认配置
    return {
      host: 'localhost',
      port: 6379,
      password: '',
      db: 0
    };
  }

  /**
   * 初始化服务
   */
  async init() {
    try {
      // 创建Redis客户端
      this._client = new redis(this._config);

      // 注册事件处理
      this._client.on('connect', () => {
        console.log(`[Redis] 已连接到Redis服务器: ${this._config.host}:${this._config.port}`);
      });

      this._client.on('error', (err) => {
        console.error(`[Redis] 连接错误:`, err);
      });

      this._client.on('close', () => {
        console.log(`[Redis] 连接已关闭`);
      });

      // 等待连接成功
      await this._waitForConnection();

      // 创建订阅客户端（用于发布/订阅模式）
      this._subscriber = this._client.duplicate();

      // 注册订阅事件处理
      this._subscriber.on('message', (channel, message) => {
        if (this._pubsubCallbacks.has(channel)) {
          const callbacks = this._pubsubCallbacks.get(channel);
          for (const callback of callbacks) {
            try {
              callback(message);
            } catch (error) {
              console.error(`[Redis] 处理订阅消息错误:`, error);
            }
          }
        }
      });

      console.log(`[Redis] 服务初始化完成`);
    } catch (error) {
      console.error(`[Redis] 初始化失败:`, error);
      throw error;
    }
  }

  /**
   * 等待Redis连接成功
   * @returns {Promise<void>}
   * @private
   */
  async _waitForConnection() {
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('连接Redis超时'));
      }, 10000);

      const checkConnection = async () => {
        try {
          await this._client.ping();
          clearTimeout(timeout);
          resolve();
        } catch (err) {
          setTimeout(checkConnection, 500);
        }
      };

      checkConnection();
    });
  }

  /**
   * 获取Redis客户端实例
   * @returns {Object} Redis客户端
   */
  getClient() {
    return this._client;
  }

  /**
   * 设置键值
   * @param {string} key 键
   * @param {string|number|object} value 值
   * @param {number} [expireSeconds] 过期时间（秒）
   * @returns {Promise<boolean>} 是否成功
   */
  async set(key, value, expireSeconds) {
    try {
      // 如果值是对象，转换为JSON字符串
      const stringValue = typeof value === 'object' ? JSON.stringify(value) : value.toString();

      if (expireSeconds) {
        await this._client.set(key, stringValue, 'EX', expireSeconds);
      } else {
        await this._client.set(key, stringValue);
      }

      if (this.isDebug) {
        console.debug(`[Redis] SET ${key} = ${stringValue.substring(0, 100)}${stringValue.length > 100 ? '...' : ''}`);
      }

      return true;
    } catch (error) {
      console.error(`[Redis] SET ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 获取键值
   * @param {string} key 键
   * @param {boolean} [parseJson=true] 是否尝试解析JSON
   * @returns {Promise<*>} 值
   */
  async get(key, parseJson = true) {
    try {
      const value = await this._client.get(key);

      if (this.isDebug) {
        if (value) {
          console.debug(`[Redis] GET ${key} = ${value.substring(0, 100)}${value.length > 100 ? '...' : ''}`);
        } else {
          console.debug(`[Redis] GET ${key} = null`);
        }
      }

      if (value && parseJson) {
        try {
          return JSON.parse(value);
        } catch (e) {
          // 如果不是有效的JSON，返回原始值
          return value;
        }
      }

      return value;
    } catch (error) {
      console.error(`[Redis] GET ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 删除键
   * @param {string|string[]} keys 键或键数组
   * @returns {Promise<number>} 删除的键数量
   */
  async del(keys) {
    try {
      const result = await this._client.del(keys);

      if (this.isDebug) {
        const keyString = Array.isArray(keys) ? keys.join(', ') : keys;
        console.debug(`[Redis] DEL ${keyString} = ${result}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] DEL 错误:`, error);
      throw error;
    }
  }

  /**
   * 检查键是否存在
   * @param {string} key 键
   * @returns {Promise<boolean>} 是否存在
   */
  async exists(key) {
    try {
      const result = await this._client.exists(key);

      if (this.isDebug) {
        console.debug(`[Redis] EXISTS ${key} = ${result}`);
      }

      return result === 1;
    } catch (error) {
      console.error(`[Redis] EXISTS ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 设置键的过期时间
   * @param {string} key 键
   * @param {number} seconds 过期时间（秒）
   * @returns {Promise<boolean>} 是否成功
   */
  async expire(key, seconds) {
    try {
      const result = await this._client.expire(key, seconds);

      if (this.isDebug) {
        console.debug(`[Redis] EXPIRE ${key} ${seconds} = ${result}`);
      }

      return result === 1;
    } catch (error) {
      console.error(`[Redis] EXPIRE ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 增加键的值
   * @param {string} key 键
   * @param {number} [increment=1] 增量
   * @returns {Promise<number>} 增加后的值
   */
  async incr(key, increment = 1) {
    try {
      let result;

      if (increment === 1) {
        result = await this._client.incr(key);
      } else {
        result = await this._client.incrby(key, increment);
      }

      if (this.isDebug) {
        console.debug(`[Redis] INCR ${key} ${increment} = ${result}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] INCR ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 减少键的值
   * @param {string} key 键
   * @param {number} [decrement=1] 减量
   * @returns {Promise<number>} 减少后的值
   */
  async decr(key, decrement = 1) {
    try {
      let result;

      if (decrement === 1) {
        result = await this._client.decr(key);
      } else {
        result = await this._client.decrby(key, decrement);
      }

      if (this.isDebug) {
        console.debug(`[Redis] DECR ${key} ${decrement} = ${result}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] DECR ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 获取哈希表中字段的值
   * @param {string} key 键
   * @param {string} field 字段
   * @param {boolean} [parseJson=true] 是否尝试解析JSON
   * @returns {Promise<*>} 值
   */
  async hget(key, field, parseJson = true) {
    try {
      const value = await this._client.hget(key, field);

      if (this.isDebug) {
        if (value) {
          console.debug(`[Redis] HGET ${key} ${field} = ${value.substring(0, 100)}${value.length > 100 ? '...' : ''}`);
        } else {
          console.debug(`[Redis] HGET ${key} ${field} = null`);
        }
      }

      if (value && parseJson) {
        try {
          return JSON.parse(value);
        } catch (e) {
          // 如果不是有效的JSON，返回原始值
          return value;
        }
      }

      return value;
    } catch (error) {
      console.error(`[Redis] HGET ${key} ${field} 错误:`, error);
      throw error;
    }
  }

  /**
   * 设置哈希表字段的值
   * @param {string} key 键
   * @param {string} field 字段
   * @param {string|number|object} value 值
   * @returns {Promise<boolean>} 是否成功
   */
  async hset(key, field, value) {
    try {
      // 如果值是对象，转换为JSON字符串
      const stringValue = typeof value === 'object' ? JSON.stringify(value) : value.toString();

      const result = await this._client.hset(key, field, stringValue);

      if (this.isDebug) {
        console.debug(`[Redis] HSET ${key} ${field} = ${stringValue.substring(0, 100)}${stringValue.length > 100 ? '...' : ''}`);
      }

      return result === 1;
    } catch (error) {
      console.error(`[Redis] HSET ${key} ${field} 错误:`, error);
      throw error;
    }
  }

  /**
   * 获取哈希表中的所有字段和值
   * @param {string} key 键
   * @param {boolean} [parseJson=true] 是否尝试解析JSON
   * @returns {Promise<object>} 字段和值的对象
   */
  async hgetall(key, parseJson = true) {
    try {
      const result = await this._client.hgetall(key);

      if (this.isDebug) {
        console.debug(`[Redis] HGETALL ${key} = ${JSON.stringify(result).substring(0, 100)}...`);
      }

      if (result && parseJson) {
        // 尝试将所有字段的值解析为JSON
        for (const field in result) {
          try {
            result[field] = JSON.parse(result[field]);
          } catch (e) {
            // 如果不是有效的JSON，保持原样
          }
        }
      }

      return result;
    } catch (error) {
      console.error(`[Redis] HGETALL ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 删除哈希表中的字段
   * @param {string} key 键
   * @param {string|string[]} fields 字段或字段数组
   * @returns {Promise<number>} 删除的字段数量
   */
  async hdel(key, fields) {
    try {
      const result = await this._client.hdel(key, fields);

      if (this.isDebug) {
        const fieldString = Array.isArray(fields) ? fields.join(', ') : fields;
        console.debug(`[Redis] HDEL ${key} ${fieldString} = ${result}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] HDEL ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 检查哈希表中字段是否存在
   * @param {string} key 键
   * @param {string} field 字段
   * @returns {Promise<boolean>} 是否存在
   */
  async hexists(key, field) {
    try {
      const result = await this._client.hexists(key, field);

      if (this.isDebug) {
        console.debug(`[Redis] HEXISTS ${key} ${field} = ${result}`);
      }

      return result === 1;
    } catch (error) {
      console.error(`[Redis] HEXISTS ${key} ${field} 错误:`, error);
      throw error;
    }
  }

  /**
   * 向列表头部添加元素
   * @param {string} key 键
   * @param {string|number|object} value 值
   * @returns {Promise<number>} 添加后的列表长度
   */
  async lpush(key, value) {
    try {
      // 如果值是对象，转换为JSON字符串
      const stringValue = typeof value === 'object' ? JSON.stringify(value) : value.toString();

      const result = await this._client.lpush(key, stringValue);

      if (this.isDebug) {
        console.debug(`[Redis] LPUSH ${key} = ${stringValue.substring(0, 100)}${stringValue.length > 100 ? '...' : ''}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] LPUSH ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 向列表尾部添加元素
   * @param {string} key 键
   * @param {string|number|object} value 值
   * @returns {Promise<number>} 添加后的列表长度
   */
  async rpush(key, value) {
    try {
      // 如果值是对象，转换为JSON字符串
      const stringValue = typeof value === 'object' ? JSON.stringify(value) : value.toString();

      const result = await this._client.rpush(key, stringValue);

      if (this.isDebug) {
        console.debug(`[Redis] RPUSH ${key} = ${stringValue.substring(0, 100)}${stringValue.length > 100 ? '...' : ''}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] RPUSH ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 获取列表元素
   * @param {string} key 键
   * @param {number} start 开始索引
   * @param {number} stop 结束索引
   * @param {boolean} [parseJson=true] 是否尝试解析JSON
   * @returns {Promise<array>} 元素数组
   */
  async lrange(key, start, stop, parseJson = true) {
    try {
      const result = await this._client.lrange(key, start, stop);

      if (this.isDebug) {
        console.debug(`[Redis] LRANGE ${key} ${start} ${stop} = ${result.length} items`);
      }

      if (result && parseJson) {
        // 尝试将所有元素解析为JSON
        return result.map(item => {
          try {
            return JSON.parse(item);
          } catch (e) {
            // 如果不是有效的JSON，返回原始值
            return item;
          }
        });
      }

      return result;
    } catch (error) {
      console.error(`[Redis] LRANGE ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 获取列表长度
   * @param {string} key 键
   * @returns {Promise<number>} 列表长度
   */
  async llen(key) {
    try {
      const result = await this._client.llen(key);

      if (this.isDebug) {
        console.debug(`[Redis] LLEN ${key} = ${result}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] LLEN ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 添加成员到集合
   * @param {string} key 键
   * @param {string|number|object} member 成员
   * @returns {Promise<number>} 添加的成员数量
   */
  async sadd(key, member) {
    try {
      // 如果值是对象，转换为JSON字符串
      const stringMember = typeof member === 'object' ? JSON.stringify(member) : member.toString();

      const result = await this._client.sadd(key, stringMember);

      if (this.isDebug) {
        console.debug(`[Redis] SADD ${key} = ${stringMember.substring(0, 100)}${stringMember.length > 100 ? '...' : ''}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] SADD ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 获取集合的所有成员
   * @param {string} key 键
   * @param {boolean} [parseJson=true] 是否尝试解析JSON
   * @returns {Promise<array>} 成员数组
   */
  async smembers(key, parseJson = true) {
    try {
      const result = await this._client.smembers(key);

      if (this.isDebug) {
        console.debug(`[Redis] SMEMBERS ${key} = ${result.length} items`);
      }

      if (result && parseJson) {
        // 尝试将所有成员解析为JSON
        return result.map(item => {
          try {
            return JSON.parse(item);
          } catch (e) {
            // 如果不是有效的JSON，返回原始值
            return item;
          }
        });
      }

      return result;
    } catch (error) {
      console.error(`[Redis] SMEMBERS ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 检查成员是否在集合中
   * @param {string} key 键
   * @param {string|number|object} member 成员
   * @returns {Promise<boolean>} 是否存在
   */
  async sismember(key, member) {
    try {
      // 如果值是对象，转换为JSON字符串
      const stringMember = typeof member === 'object' ? JSON.stringify(member) : member.toString();

      const result = await this._client.sismember(key, stringMember);

      if (this.isDebug) {
        console.debug(`[Redis] SISMEMBER ${key} ${stringMember.substring(0, 50)} = ${result}`);
      }

      return result === 1;
    } catch (error) {
      console.error(`[Redis] SISMEMBER ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 从集合中移除成员
   * @param {string} key 键
   * @param {string|number|object} member 成员
   * @returns {Promise<number>} 移除的成员数量
   */
  async srem(key, member) {
    try {
      // 如果值是对象，转换为JSON字符串
      const stringMember = typeof member === 'object' ? JSON.stringify(member) : member.toString();

      const result = await this._client.srem(key, stringMember);

      if (this.isDebug) {
        console.debug(`[Redis] SREM ${key} ${stringMember.substring(0, 50)} = ${result}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] SREM ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 获取集合的成员数量
   * @param {string} key 键
   * @returns {Promise<number>} 成员数量
   */
  async scard(key) {
    try {
      const result = await this._client.scard(key);

      if (this.isDebug) {
        console.debug(`[Redis] SCARD ${key} = ${result}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] SCARD ${key} 错误:`, error);
      throw error;
    }
  }

  /**
   * 使用Lua脚本
   * @param {string} script Lua脚本
   * @param {number} numKeys 键的数量
   * @param {Array} args 参数数组
   * @returns {Promise<*>} 脚本执行结果
   */
  async eval(script, numKeys, args) {
    try {
      const result = await this._client.eval(script, numKeys, ...args);

      if (this.isDebug) {
        console.debug(`[Redis] EVAL script with ${args.length} args`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] EVAL 错误:`, error);
      throw error;
    }
  }

  /**
   * 执行事务
   * @param {Function} callback 事务回调函数，接收一个事务对象
   * @returns {Promise<Array>} 事务执行结果
   */
  async transaction(callback) {
    const multi = this._client.multi();

    try {
      // 执行回调函数来添加命令
      await callback(multi);

      // 执行事务
      const results = await multi.exec();

      if (this.isDebug) {
        console.debug(`[Redis] TRANSACTION with ${results ? results.length : 0} commands`);
      }

      // 处理结果
      if (results) {
        return results.map(([err, result]) => {
          if (err) throw err;
          return result;
        });
      }

      return [];
    } catch (error) {
      console.error(`[Redis] TRANSACTION 错误:`, error);
      throw error;
    }
  }

  /**
   * 发布消息
   * @param {string} channel 频道
   * @param {string|object} message 消息
   * @returns {Promise<number>} 接收消息的客户端数量
   */
  async publish(channel, message) {
    try {
      // 如果消息是对象，转换为JSON字符串
      const stringMessage = typeof message === 'object' ? JSON.stringify(message) : message.toString();

      const result = await this._client.publish(channel, stringMessage);

      if (this.isDebug) {
        console.debug(`[Redis] PUBLISH ${channel} = ${stringMessage.substring(0, 100)}${stringMessage.length > 100 ? '...' : ''}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] PUBLISH ${channel} 错误:`, error);
      throw error;
    }
  }

  /**
   * 订阅频道
   * @param {string} channel 频道
   * @param {Function} callback 消息回调函数
   * @returns {Promise<void>}
   */
  async subscribe(channel, callback) {
    try {
      // 添加回调到映射
      if (!this._pubsubCallbacks.has(channel)) {
        this._pubsubCallbacks.set(channel, []);

        // 首次订阅时，调用Redis订阅
        await this._subscriber.subscribe(channel);

        if (this.isDebug) {
          console.debug(`[Redis] SUBSCRIBE ${channel}`);
        }
      }

      // 添加回调
      this._pubsubCallbacks.get(channel).push(callback);
    } catch (error) {
      console.error(`[Redis] SUBSCRIBE ${channel} 错误:`, error);
      throw error;
    }
  }

  /**
   * 取消订阅频道
   * @param {string} channel 频道
   * @param {Function} [callback] 特定的回调函数，如果未提供则取消所有回调
   * @returns {Promise<void>}
   */
  async unsubscribe(channel, callback) {
    try {
      if (this._pubsubCallbacks.has(channel)) {
        if (callback) {
          // 移除特定回调
          const callbacks = this._pubsubCallbacks.get(channel);
          const index = callbacks.indexOf(callback);

          if (index !== -1) {
            callbacks.splice(index, 1);
          }

          // 如果没有更多回调，取消订阅
          if (callbacks.length === 0) {
            this._pubsubCallbacks.delete(channel);
            await this._subscriber.unsubscribe(channel);

            if (this.isDebug) {
              console.debug(`[Redis] UNSUBSCRIBE ${channel}`);
            }
          }
        } else {
          // 移除所有回调并取消订阅
          this._pubsubCallbacks.delete(channel);
          await this._subscriber.unsubscribe(channel);

          if (this.isDebug) {
            console.debug(`[Redis] UNSUBSCRIBE ${channel} (all callbacks)`);
          }
        }
      }
    } catch (error) {
      console.error(`[Redis] UNSUBSCRIBE ${channel} 错误:`, error);
      throw error;
    }
  }

  /**
   * 获取所有匹配的键
   * @param {string} pattern 模式
   * @returns {Promise<Array<string>>} 键数组
   */
  async keys(pattern) {
    try {
      const result = await this._client.keys(pattern);

      if (this.isDebug) {
        console.debug(`[Redis] KEYS ${pattern} = ${result.length} keys`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] KEYS ${pattern} 错误:`, error);
      throw error;
    }
  }

  /**
   * 清空当前数据库
   * @returns {Promise<string>} 操作结果
   */
  async flushdb() {
    try {
      const result = await this._client.flushdb();

      if (this.isDebug) {
        console.debug(`[Redis] FLUSHDB = ${result}`);
      }

      return result;
    } catch (error) {
      console.error(`[Redis] FLUSHDB 错误:`, error);
      throw error;
    }
  }

  /**
   * 停止服务
   */
  async stop() {
    try {
      // 关闭订阅客户端
      if (this._subscriber) {
        this._subscriber.disconnect();
        this._subscriber = null;
      }

      // 关闭主客户端
      if (this._client) {
        this._client.disconnect();
        this._client = null;
      }

      console.log('[Redis] 服务已停止');
    } catch (error) {
      console.error('[Redis] 停止服务时发生错误:', error);
    }
  }
}

module.exports = Redis; 